package com.feishi.core.socket.netty;


import com.feishi.core.analytic.assembler.BaseAssembler;
import com.feishi.core.socket.common.SocketClient;
import com.feishi.core.socket.common.SocketDecoder;
import com.feishi.core.socket.common.decoder.SplitDecoder;
import com.feishi.core.socket.common.lifecycle.SocketApplication;
import com.feishi.core.socket.common.lifecycle.SocketSession;
import com.feishi.core.socket.netty.handler.NettyDecoderHandler;
import com.feishi.core.socket.netty.handler.NettyHandler;
import com.feishi.core.util.common.bean.CallBack;
import com.feishi.core.util.timer.CycleExecutor;
import com.feishi.core.util.timer.Executable;
import com.feishi.core.util.timer.ExecuteRule;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.ssl.SslHandler;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import java.util.ArrayList;
import java.util.List;

public abstract class NettyClient<T extends BaseAssembler> extends SocketClient<T> {

	protected Bootstrap bootstrap;
	protected EventLoopGroup eventLoopGroup;
	private boolean reConnectable=false;
	private SSLEngine engine;
	private boolean enableSSL;
	private CallBack<NettyClient> connectCallBack;

	public NettyClient() {
		init();
	}
	public void startConnect(CallBack<NettyClient> callBack){
		this.connectCallBack=callBack;
		startConnect();
	}
	public void startConnect() {
		bootstrap=new Bootstrap();
		eventLoopGroup=new NioEventLoopGroup();
		try {
			bootstrap.channel(NioSocketChannel.class);
			bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
			bootstrap.group(eventLoopGroup);
			bootstrap.remoteAddress(getHost(), getPort());
			bootstrap.handler(new ChannelInitializer<SocketChannel>() {
				@Override
				protected void initChannel(SocketChannel socketChannel) throws Exception {
					ChannelPipeline pipe = socketChannel.pipeline();

					if (enableSSL){
						pipe.addLast(new SslHandler(engine));
					}
					if(reConnectable){
						pipe.addLast(getReConnectableHandler());
					}

					session=new SocketSession(application);
					for (ChannelHandlerFactory factory:handlerFactoryList) {
						pipe.addLast(factory.getChannelHandler(session));
					}
				}
			});
			connect();
		} finally {
			eventLoopGroup.shutdownGracefully();
		}
	}

	private ChannelHandler getReConnectableHandler() {
		NettyClient nettyClient = this;
		ChannelHandlerAdapter handler = new ChannelHandlerAdapter() {
			@Override
			public void channelInactive(ChannelHandlerContext ctx) throws Exception {
				super.channelInactive(ctx);
				new Thread(()-> nettyClient.startConnect()).start();
			}
		};
		return handler;
	}



	public void enableReconectable(){
		reConnectable=true;
	}


	public void enableSSL(SSLContext context){
		if(context==null){
			return;
		}
		this.engine = context.createSSLEngine(getHost(),getPort());
		engine.setUseClientMode(true);
		this.enableSSL=true;
	}
	public void disableSSL(SSLContext context){
		this.enableSSL=false;
	}
	
	
	
	private ExecuteRule rule=new ExecuteRule(500,1.5f,-1);
	protected void connect()  {
		CycleExecutor.execute(new Executable() {
			@Override
			public boolean execute() {
				return connectServer();
			}
		},rule);
	}

	public void setRule(ExecuteRule rule) {
		this.rule = rule;
	}

	public boolean connectServer(){
		ChannelFuture future = null;
		try {
			future = bootstrap.connect().sync();
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (future!=null&&future.isSuccess()) {
			SocketChannel socketChannel = (SocketChannel) future.channel();
			System.out.println("----------------connect server success----------------");
			if(this.connectCallBack!=null){
				connectCallBack.execute(this);
				this.connectCallBack=null;
			}
		}else{
			return false;
		}
		try {
			future.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return true;
	}

	public void sleep(long times){
		try {
			Thread.sleep(times);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	protected List<ChannelHandlerFactory> handlerFactoryList=new ArrayList<ChannelHandlerFactory>();
	public void addChannelHandlerFactory(ChannelHandlerFactory channelHandlerFactory){
		handlerFactoryList.add(channelHandlerFactory);
	}
	public void addChannelHandlerFactoryAtFirst(ChannelHandlerFactory channelHandlerFactory){
		handlerFactoryList.add(0,channelHandlerFactory);
	}
	public void clearChannelHandlerFactory(){
		handlerFactoryList.clear();
	}
	public void init(){
		this.addChannelHandlerFactoryAtFirst((SocketSession session)->new NettyDecoderHandler(decoder));
		this.addChannelHandlerFactory((SocketSession session)->new NettyHandler(session));
	}
	private SocketDecoder<T> decoder=new SplitDecoder('\n','\\');

	public SocketDecoder<T> getDecoder() {
		return decoder;
	}

	public void setDecoder(SocketDecoder<T> decoder) {
		this.decoder = decoder;
	}

}
